<template>
  <ElForm
    ref="ElForm"
    :key="formKey"
    :model="model"
    :rules="formRules"
    :size="elFormItem.elFormItemSize"
    v-bind="formAttrs"
    @submit.native.prevent
  >
    <ElFormItem
      v-for="(item, index) in formItems"
      :key="index"
      :label="item.label"
      :prop="item.prop"
    >
      <CustomRender
        :render="item.render"
        :value="model[item.prop]"
        :model="getModel()"
        :label="item.label"
        :prop="item.prop"
        :options="item.options"
        :dicts="item.dicts"
        :style="item.width ? `width:${parseInt(item.width)}px` : undefined"
        @input="onInput(item, $event)"
      />
      <CustomRender
        :value="model[item.prop]"
        v-if="item.suffix"
        :render="item.suffix"
      />
    </ElFormItem>
  </ElForm>
</template>

<script>
import CustomRender from '../CustomRender'
import { components } from '@lib/default'
import { cloneDeep, isArray } from 'lodash-es'
import { getAndRemoveValue } from '../utils'

const defaultFormAttrs = {
  labelWidth: '100px',
  labelPosition: 'left',
  validateOnRuleChange: false
}

/**
 * rule没有设置message的话，默认一个message
 */
const setDefaultRuleMessage = (rule, component) => {
  if (isArray(rule) && rule[0]) {
    rule = rule[0]
  }
  if (!rule.message) {
    if (['radio', 'select', 'date'].includes(component)) {
      rule.message = '请选择'
    } else if (['input', 'textarea'].includes(component)) {
      rule.message = '请填写'
    } else {
      rule.message = '请设置'
    }
  }
}

export default {
  components: { CustomRender },

  props: {
    options: { type: Object, required: true }
  },

  data() {
    return {
      model: {},
      formKey: '',
    }
  },

  inject: {
    elFormItem: {
      from: 'elFormItem',
      default: () => ({})
    }
  },

  computed: {
    // 绑定给ElForm的属性
    formAttrs() {
      return Object.assign({}, defaultFormAttrs, this.options.attrs)
    },
    // 表单项
    formItems() {
      return (this.options.items || []).reduce((res, item) => {
        const show = item.show
        if (!show || show(this.model)) {
          item = cloneDeep(item)

          item.options = item.options || {}

          if (item.required && !item.rule) {
            item.rule = { required: getAndRemoveValue(item, 'required') }
          }

          if (!item.render && item.component) {
            item.render = components[item.component]
          }

          res.push(item)
        }

        return res
      }, [])
    },
    currentItems() {
      return this.formItems.map(item => item.prop).join(',')
    },
    // 规则
    formRules() {
      return this.formItems.reduce((res, item) => {
        if (item.rule) {
          const rule = cloneDeep(item.rule)
          setDefaultRuleMessage(rule, item.component)
          res[item.prop] = rule
        }
        return res
      }, {})
    }
  },

  watch: {
    currentItems: {
      handler() {
        this.syncFormModel()
      },
      immediate: true
    }
  },

  methods: {
    async validate() {
      await this.$refs.ElForm.validate()
      return this.getModel()
    },
    getModel() {
      return { ...this.model }
    },
    reset() {
      this.model = {}
      this.$nextTick(() => {
        this.syncFormModel()
        this.formKey = +new Date()
      })
    },
    onInput(formItem, value) {
      if (formItem.transform) {
        value = formItem.transform(value)
      }
      this.setValue(formItem.prop, value)
    },
    setValue(prop, value) {
      this.$set(this.model, prop, value)
    },
    /**
     * 表单项增减时，同步this.model
     */
    syncFormModel() {
      const formItemsMap = this.formItems.reduce((res, item) => {
        res[item.prop] = item
        return res
      }, {})

      for (const key in this.model) {
        if (!(key in formItemsMap)) {
          this.$delete(this.model, key)
        }
      }

      for (const key in formItemsMap) {
        if (
          !(key in this.model) &&
          ('defaultValue' in formItemsMap[key])
        ) {
          this.$set(this.model, key, formItemsMap[key].defaultValue)
        }
      }
    }
  }
}
</script>
